home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / shllutil.lha / shellutils-1.8 / src / su.c < prev    next >
C/C++ Source or Header  |  1992-10-28  |  13KB  |  520 lines

  1. /* su for GNU.  Run a shell with substitute user and group IDs.
  2.    Copyright (C) 1992 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Run a shell with the real and effective UID and GID and groups
  19.    of USER, default `root'.
  20.  
  21.    The shell run is taken from USER's password entry, /bin/sh if
  22.    none is specified there.  If the account has a password, su
  23.    prompts for a password unless run by a user with real UID 0.
  24.  
  25.    Does not change the current directory.
  26.    Sets `HOME' and `SHELL' from the password entry for USER, and if
  27.    USER is not root, sets `USER' and `LOGNAME' to USER.
  28.    The subshell is not a login shell.
  29.  
  30.    If one or more ARGs are given, they are passed as additional
  31.    arguments to the subshell.
  32.  
  33.    Does not handle /bin/sh or other shells specially
  34.    (setting argv[0] to "-su", passing -c only to certain shells, etc.).
  35.    I don't see the point in doing that, and it's ugly.
  36.  
  37.    This program intentionally does not support a "wheel group" that
  38.    restricts who can su to UID 0 accounts.  RMS considers that to
  39.    be fascist.
  40.  
  41.    Options:
  42.    -, -l, --login    Make the subshell a login shell.
  43.             Unset all environment variables except
  44.             TERM, HOME and SHELL (set as above), and USER
  45.             and LOGNAME (set unconditionally as above), and
  46.             set PATH to a default value.
  47.             Change to USER's home directory.
  48.             Prepend "-" to the shell's name.
  49.    -c, --commmand=COMMAND
  50.             Pass COMMAND to the subshell with a -c option
  51.             instead of starting an interactive shell.
  52.    -f, --fast        Pass the -f option to the subshell.
  53.    -m, -p, --preserve-environment
  54.             Do not change HOME, USER, LOGNAME, SHELL.
  55.             Run $SHELL instead of USER's shell from /etc/passwd
  56.             unless not the superuser and USER's shell is
  57.             restricted.
  58.             Overridden by --login and --shell.
  59.    -s, --shell=shell    Run SHELL instead of USER's shell from /etc/passwd
  60.             unless not the superuser and USER's shell is
  61.             restricted.
  62.  
  63.    Compile-time options:
  64.    -DSYSLOG_SUCCESS    Log successful su's (by default, to root) with syslog.
  65.    -DSYSLOG_FAILURE    Log failed su's (by default, to root) with syslog.
  66.  
  67.    -DSYSLOG_NON_ROOT    Log all su's, not just those to root (UID 0).
  68.    Never logs attempted su's to nonexistent accounts.
  69.  
  70.    Written by David MacKenzie <djm@gnu.ai.mit.edu>.  */
  71.  
  72. #include <stdio.h>
  73. #include <getopt.h>
  74. #include <sys/types.h>
  75. #include <pwd.h>
  76. #include "system.h"
  77.  
  78. #ifdef HAVE_SYSLOG_H
  79. #include <syslog.h>
  80. void log_su ();
  81. #else
  82. #ifdef SYSLOG_SUCCESS
  83. #undef SYSLOG_SUCCESS
  84. #endif
  85. #ifdef SYSLOG_FAILURE
  86. #undef SYSLOG_FAILURE
  87. #endif
  88. #ifdef SYSLOG_NON_ROOT
  89. #undef SYSLOG_NON_ROOT
  90. #endif
  91. #endif
  92.  
  93. #ifdef _POSIX_VERSION
  94. #include <limits.h>
  95. #ifdef NGROUPS_MAX
  96. #undef NGROUPS_MAX
  97. #endif
  98. #define NGROUPS_MAX sysconf (_SC_NGROUPS_MAX)
  99. #else /* not _POSIX_VERSION */
  100. struct passwd *getpwuid ();
  101. struct group *getgrgid ();
  102. uid_t getuid ();
  103. #include <sys/param.h>
  104. #if !defined(NGROUPS_MAX) && defined(NGROUPS)
  105. #define NGROUPS_MAX NGROUPS
  106. #endif
  107. #endif /* not _POSIX_VERSION */
  108.  
  109. #ifdef _POSIX_SOURCE
  110. #define endgrent()
  111. #define endpwent()
  112. #endif
  113.  
  114. #ifdef HAVE_SHADOW_H
  115. #include <shadow.h>
  116. #endif
  117.  
  118. /* The default PATH for simulated logins to non-superuser accounts.  */
  119. #define DEFAULT_LOGIN_PATH ":/usr/ucb:/bin:/usr/bin"
  120.  
  121. /* The default PATH for simulated logins to superuser accounts.  */
  122. #define DEFAULT_ROOT_LOGIN_PATH "/usr/ucb:/bin:/usr/bin:/etc"
  123.  
  124. /* The shell to run if none is given in the user's passwd entry.  */
  125. #define DEFAULT_SHELL "/bin/sh"
  126.  
  127. /* The user to become if none is specified.  */
  128. #define DEFAULT_USER "root"
  129.  
  130. char *crypt ();
  131. char *getpass ();
  132. char *getusershell ();
  133. void endusershell ();
  134. void setusershell ();
  135.  
  136. char *basename ();
  137. char *concat ();
  138. char *xmalloc ();
  139. char *xrealloc ();
  140. int correct_password ();
  141. int elements ();
  142. int restricted_shell ();
  143. void change_identity ();
  144. void error ();
  145. void modify_environment ();
  146. void run_shell ();
  147. void usage ();
  148. void xputenv ();
  149.  
  150. extern char **environ;
  151.  
  152. /* The name this program was run with.  */
  153. char *program_name;
  154.  
  155. /* If nonzero, pass the `-f' option to the subshell.  */
  156. int fast_startup;
  157.  
  158. /* If nonzero, simulate a login instead of just starting a shell.  */
  159. int simulate_login;
  160.  
  161. /* If nonzero, change some environment vars to indicate the user su'd to.  */
  162. int change_environment;
  163.  
  164. struct option longopts[] =
  165. {
  166.   {"command", 1, 0, 'c'},
  167.   {"fast", 0, &fast_startup, 1},
  168.   {"login", 0, &simulate_login, 1},
  169.   {"preserve-environment", 0, &change_environment, 0},
  170.   {"shell", 1, 0, 's'},
  171.   {0, 0, 0, 0}
  172. };
  173.  
  174. void
  175. main (argc, argv)
  176.      int argc;
  177.      char **argv;
  178. {
  179.   int optc;
  180.   char *new_user = DEFAULT_USER;
  181.   char *command = 0;
  182.   char **additional_args = 0;
  183.   char *shell = 0;
  184.   struct passwd *pw;
  185.  
  186.   program_name = argv[0];
  187.   fast_startup = 0;
  188.   simulate_login = 0;
  189.   change_environment = 1;
  190.  
  191.   while ((optc = getopt_long (argc, argv, "c:flmps:", longopts, (int *) 0))
  192.      != EOF)
  193.     {
  194.       switch (optc)
  195.     {
  196.     case 0:
  197.       break;
  198.     case 'c':
  199.       command = optarg;
  200.       break;
  201.     case 'f':
  202.       fast_startup = 1;
  203.       break;
  204.     case 'l':
  205.       simulate_login = 1;
  206.       break;
  207.     case 'm':
  208.     case 'p':
  209.       change_environment = 0;
  210.       break;
  211.     case 's':
  212.       shell = optarg;
  213.       break;
  214.     default:
  215.       usage ();
  216.     }
  217.     }
  218.   if (optind < argc && !strcmp (argv[optind], "-"))
  219.     {
  220.       simulate_login = 1;
  221.       ++optind;
  222.     }
  223.   if (optind < argc)
  224.     new_user = argv[optind++];
  225.   if (optind < argc)
  226.     additional_args = argv + optind;
  227.  
  228.   pw = getpwnam (new_user);
  229.   if (pw == 0)
  230.     error (1, 0, "user %s does not exist", new_user);
  231.   endpwent ();
  232.   if (!correct_password (pw))
  233.     {
  234. #ifdef SYSLOG_FAILURE
  235.       log_su (pw, 0);
  236. #endif
  237.       error (1, 0, "incorrect password");
  238.     }
  239. #ifdef SYSLOG_SUCCESS
  240.   else
  241.     {
  242.       log_su (pw, 1);
  243.     }
  244. #endif
  245.  
  246.   if (pw->pw_shell == 0 || pw->pw_shell[0] == 0)
  247.     pw->pw_shell = DEFAULT_SHELL;
  248.   if (shell == 0 && change_environment == 0)
  249.     shell = getenv ("SHELL");
  250.   if (shell != 0 && getuid () && restricted_shell (pw->pw_shell))
  251.     {
  252.       /* The user being su'd to has a nonstandard shell, and so is
  253.      probably a uucp account or has restricted access.  Don't
  254.      compromise the account by allowing access with a standard
  255.      shell.  */
  256.       error (0, 0, "using restricted shell %s", pw->pw_shell);
  257.       shell = 0;
  258.     }
  259.   if (shell == 0)
  260.     shell = pw->pw_shell;
  261.   shell = strcpy (xmalloc (strlen (shell) + 1), shell);
  262.   modify_environment (pw, shell);
  263.  
  264.   change_identity (pw);
  265.   if (simulate_login && chdir (pw->pw_dir))
  266.     error (0, errno, "warning: cannot change directory to %s", pw->pw_dir);
  267.   run_shell (shell, command, additional_args);
  268. }
  269.  
  270. /* Ask the user for a password.
  271.    Return 1 if the user gives the correct password for entry PW,
  272.    0 if not.  Return 1 without asking for a password if run by UID 0
  273.    or if PW has an empty password.  */
  274.  
  275. int
  276. correct_password (pw)
  277.      struct passwd *pw;
  278. {
  279.   char *unencrypted, *encrypted, *correct;
  280. #ifdef HAVE_SHADOW_H
  281.   /* Shadow passwd stuff for SVR3 and maybe other systems.  */
  282.   struct spwd *sp = getspnam (pw->pw_name);
  283.  
  284.   endspent ();
  285.   if (sp)
  286.     correct = sp->sp_pwdp;
  287.   else
  288. #endif
  289.   correct = pw->pw_passwd;
  290.  
  291.   if (getuid () == 0 || correct == 0 || correct[0] == '\0')
  292.     return 1;
  293.  
  294.   unencrypted = getpass ("Password:");
  295.   encrypted = crypt (unencrypted, correct);
  296.   bzero (unencrypted, strlen (unencrypted));
  297.   return strcmp (encrypted, correct) == 0;
  298. }
  299.  
  300. /* Update `environ' for the new shell based on PW, with SHELL being
  301.    the value for the SHELL environment variable.  */
  302.  
  303. void
  304. modify_environment (pw, shell)
  305.      struct passwd *pw;
  306.      char *shell;
  307. {
  308.   char *term;
  309.  
  310.   if (simulate_login)
  311.     {
  312.       /* Leave TERM unchanged.  Set HOME, SHELL, USER, LOGNAME, PATH.
  313.          Unset all other environment variables.  */
  314.       term = getenv ("TERM");
  315.       environ = (char **) xmalloc (2 * sizeof (char *));
  316.       environ[0] = 0;
  317.       if (term)
  318.     xputenv (concat ("TERM", "=", term));
  319.       xputenv (concat ("HOME", "=", pw->pw_dir));
  320.       xputenv (concat ("SHELL", "=", shell));
  321.       xputenv (concat ("USER", "=", pw->pw_name));
  322.       xputenv (concat ("LOGNAME", "=", pw->pw_name));
  323.       xputenv (concat ("PATH", "=", pw->pw_uid
  324.                ? DEFAULT_LOGIN_PATH : DEFAULT_ROOT_LOGIN_PATH));
  325.     }
  326.   else
  327.     {
  328.       /* Set HOME, SHELL, and if not becoming a super-user,
  329.      USER and LOGNAME.  */
  330.       if (change_environment)
  331.     {
  332.       xputenv (concat ("HOME", "=", pw->pw_dir));
  333.       xputenv (concat ("SHELL", "=", shell));
  334.       if (pw->pw_uid)
  335.         {
  336.           xputenv (concat ("USER", "=", pw->pw_name));
  337.           xputenv (concat ("LOGNAME", "=", pw->pw_name));
  338.         }
  339.     }
  340.     }
  341. }
  342.  
  343. /* Become the user and group(s) specified by PW.  */
  344.  
  345. void
  346. change_identity (pw)
  347.      struct passwd *pw;
  348. {
  349. #ifdef NGROUPS_MAX
  350.   errno = 0;
  351.   if (initgroups (pw->pw_name, pw->pw_gid) == -1)
  352.     error (1, errno, "cannot set groups");
  353.   endgrent ();
  354. #endif
  355.   if (setgid (pw->pw_gid))
  356.     error (1, errno, "cannot set group id");
  357.   if (setuid (pw->pw_uid))
  358.     error (1, errno, "cannot set user id");
  359. }
  360.  
  361. /* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
  362.    If COMMAND is nonzero, pass it to the shell with the -c option.
  363.    If ADDITIONAL_ARGS is nonzero, pass it to the shell as more
  364.    arguments.  */
  365.  
  366. void
  367. run_shell (shell, command, additional_args)
  368.      char *shell;
  369.      char *command;
  370.      char **additional_args;
  371. {
  372.   char **args;
  373.   int argno = 1;
  374.  
  375.   if (additional_args)
  376.     args = (char **) xmalloc (sizeof (char *)
  377.                   * (10 + elements (additional_args)));
  378.   else
  379.     args = (char **) xmalloc (sizeof (char *) * 10);
  380.   if (simulate_login)
  381.     {
  382.       args[0] = xmalloc (strlen (shell) + 2);
  383.       args[0][0] = '-';
  384.       strcpy (args[0] + 1, basename (shell));
  385.     }
  386.   else
  387.     args[0] = basename (shell);
  388.   if (fast_startup)
  389.     args[argno++] = "-f";
  390.   if (command)
  391.     {
  392.       args[argno++] = "-c";
  393.       args[argno++] = command;
  394.     }
  395.   if (additional_args)
  396.     for (; *additional_args; ++additional_args)
  397.       args[argno++] = *additional_args;
  398.   args[argno] = 0;
  399.   execv (shell, args);
  400.   error (1, errno, "cannot run %s", shell);
  401. }
  402.  
  403. #if defined (SYSLOG_SUCCESS) || defined (SYSLOG_FAILURE)
  404. /* Log the fact that someone has run su to the user given by PW;
  405.    if SUCCESSFUL is nonzero, they gave the correct password, etc.  */
  406.  
  407. void
  408. log_su (pw, successful)
  409.      struct passwd *pw;
  410.      int successful;
  411. {
  412.   char *new_user, *old_user, *tty;
  413.  
  414. #ifndef SYSLOG_NON_ROOT
  415.   if (pw->pw_uid)
  416.     return;
  417. #endif
  418.   new_user = pw->pw_name;
  419.   /* The utmp entry (via getlogin) is probably the best way to identify
  420.      the user, especially if someone su's from a su-shell.  */
  421.   old_user = getlogin ();
  422.   if (old_user == 0)
  423.     old_user = "";
  424.   tty = ttyname (2);
  425.   if (tty == 0)
  426.     tty = "";
  427.   /* 4.2BSD openlog doesn't have the third parameter.  */
  428.   openlog (basename (program_name), 0
  429. #ifdef LOG_AUTH
  430.        , LOG_AUTH
  431. #endif
  432.        );
  433.   syslog (LOG_NOTICE,
  434. #ifdef SYSLOG_NON_ROOT
  435.       "%s(to %s) %s on %s",
  436. #else
  437.       "%s%s on %s",
  438. #endif
  439.       successful ? "" : "FAILED SU ",
  440. #ifdef SYSLOG_NON_ROOT
  441.       new_user,
  442. #endif
  443.       old_user, tty);
  444.   closelog ();
  445. }
  446. #endif
  447.  
  448. /* Return 1 if SHELL is a restricted shell (one not returned by
  449.    getusershell), else 0, meaning it is a standard shell.  */
  450.  
  451. int
  452. restricted_shell (shell)
  453.      char *shell;
  454. {
  455.   char *line;
  456.  
  457.   setusershell ();
  458.   while (line = getusershell ())
  459.     {
  460.       if (*line != '#' && strcmp (line, shell) == 0)
  461.     {
  462.       endusershell ();
  463.       return 0;
  464.     }
  465.     }
  466.   endusershell ();
  467.   return 1;
  468. }
  469.  
  470. /* Return the number of elements in ARR, a null-terminated array.  */
  471.  
  472. int
  473. elements (arr)
  474.      char **arr;
  475. {
  476.   int n = 0;
  477.  
  478.   for (n = 0; *arr; ++arr)
  479.     ++n;
  480.   return n;
  481. }
  482.  
  483. /* Add VAL to the environment, checking for out of memory errors.  */
  484.  
  485. void
  486. xputenv (val)
  487.      char *val;
  488. {
  489.   if (putenv (val))
  490.     error (1, 0, "virtual memory exhausted");
  491. }
  492.  
  493. /* Return a newly-allocated string whose contents concatenate
  494.    those of S1, S2, S3.  */
  495.  
  496. char *
  497. concat (s1, s2, s3)
  498.      char *s1, *s2, *s3;
  499. {
  500.   int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
  501.   char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
  502.  
  503.   strcpy (result, s1);
  504.   strcpy (result + len1, s2);
  505.   strcpy (result + len1 + len2, s3);
  506.   result[len1 + len2 + len3] = 0;
  507.  
  508.   return result;
  509. }
  510.  
  511. void
  512. usage ()
  513. {
  514.   fprintf (stderr, "\
  515. Usage: %s [-flmp] [-c command] [-s shell] [--login] [--fast]\n\
  516.        [--preserve-environment] [--command=command] [--shell=shell] [-]\n\
  517.        [user [arg...]]\n", program_name);
  518.   exit (1);
  519. }
  520.